import { Material } from "./Material";
import { any, has } from "../core/logic";
import { Buffer } from "../gpu/types/Buffer";
import { textureLoader } from "../assets/loader/TextureLoader";
import { color } from "../core/color";
import { Engine } from "../Engine";
/**
* 材质类。
* @class
* @memberof w.material
*/
class PointMaterial extends Material {
  /**
   * 创建基础材质
   * @constructor
   * @param {*} opts 
   * @param {String} opts.alphaMap 【可选，缺省:undefine】透明度贴图。
   * @param {String} opts.aoMap 【可选，缺省:undefine】环境光遮罩贴图。
   * @param {Number} opts.aoMapIntensity 【可选，缺省:1.0】ao强度。
   * @param {String} opts.map 【可选，缺省:undefine】基地色贴图。
   * @param {String} opts.color 【可选，缺省:"#ffffff"】基地色。
   * @param {String} opts.emissiveMap 【可选，缺省:undefine】自发光贴图。
   * @param {String} opts.emissive 【可选，缺省:"#000000"】基地色。
   * @param {String} opts.ambientMap 【可选，缺省:undefine】环境光贴图。
   * @param {String} opts.ambien 【可选，缺省:"#ffffff"】基地色。
   * @param {String} opts.diffuseMap 【可选，缺省:undefine】漫反射贴图。
   * @param {String} opts.diffuse 【可选，缺省:"#ffffff"】漫反射颜色。
   * @param {String} opts.specularMap 【可选，缺省:undefine】高光贴图。
   * @param {String} opts.specular 【可选，缺省:"#ffffff"】高光颜色。
   * @param {String} opts.normalMap 【可选，缺省:undefine】法向量贴图。
   * @param {String} opts.displacementMap 【可选，缺省:undefine】几何位置偏移贴图。
   * @param {Number} opts.displacementScale 【可选，缺省:0】几何位置偏移缩放。
   * @param {Number} opts.displacementBias 【可选，缺省:1】几何位置偏移斜率。
   * @param {Boolean} opts.sdf 【可选，缺省:false】是否启用有向距离场。
   * @param {Boolean} opts.mipMap 【可选，缺省:false】是否生成mipMap。
   * @param {Number} opts.shininess 【可选，缺省:2】光泽系数。
   */
  constructor(opts = {}, share) {
    super(opts, share);
    this.materialType = 'PointMaterial';
    this.alphaMap = opts.alphaMap;
    this.aoMap = opts.aoMap;
    this.map = opts.map;
    this.emissiveMap = opts.emissiveMap;
    this.ambientMap = opts.ambientMap;
    this.diffuseMap = opts.diffuseMap;
    this.specularMap = opts.specularMap;
    this.normalMap = opts.normalMap;
    this.displacementMap = opts.displacementMap;
    this.aoMapIntensity = any(opts.aoMapIntensity, 1.0);
    this.color = any(opts.color, '#ffffff');
    this.emissive = any(opts.emissive, '#000000');
    this.ambient = any(opts.ambient, '#ffffff');
    this.diffuse = any(opts.diffuse, '#ffffff');
    this.shininess = any(opts.shininess, 2);
    this.specular = any(opts.specular, '#ffffff');
    this.displacementScale = any(opts.displacementScale, 1);
    this.displacementBias = any(opts.displacementBias, 0);
    this.sdf = any(opts.sdf, false);
    this.cameraSide = any(opts.cameraSide, false);//是否只绘制相机一侧
    this.sdfFrom=any(opts.sdfFrom,0.65);
    this.sdfTo=any(opts.sdfTo,0.78);
    this.mipMap= any(opts.mipMap, false);
    this.autoUpdate = true;
    
    this.update();
  }
  get materialType () {
    return this._materialType;
  }
  set materialType (v) {
    this._materialType = v;
    this.addConfigParam("materialType", v);
    this.update();
    return this;
  }
  set alphaMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'alphaMap');
    this.registerTexture(v, 'alphaMap', (t) => {
      this._alphaMap = t;
      this.setState(true, 'alphaMap');
      this.addMapParam("alphaMap");
      this.update();
    })
    return this;
  }
  get alphaMap () {
    return this._alphaMap;
  }
  set aoMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'aoMap');
    this.registerTexture(v, 'aoMap', (t) => {
      this._aoMap = t;
      this.setState(true, 'aoMap');
      this.addMapParam("aoMap");
      this.update();
    })
    return this;
  }
  get aoMap () {
    return this._aoMap;
  }
  set map (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'map');
    this.registerTexture(v, 'map', (t) => {
      this._map = t;
      this.setState(true, 'map');
      this.addMapParam("map");
      this.update();
    })
    return this;
  }
  get map () {
    return this._map;
  }
  set emissiveMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'emissiveMap');
    this.registerTexture(v, 'emissiveMap', (t) => {
      this._emissiveMap = t;
      this.setState(true, 'emissiveMap');
      this.addMapParam("emissiveMap");
      this.update();
    })
    return this;
  }
  get emissiveMap () {
    return this._emissiveMap;
  }
  set ambientMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'ambientMap');
    this.registerTexture(v, 'ambientMap', (t) => {
      this._ambientMap = t;
      this.setState(true, 'ambientMap');
      this.addMapParam("ambientMap");
      this.update();
    })
    return this;
  }
  get ambientMap () {
    return this._ambientMap;
  }
  set diffuseMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'diffuseMap');
    this.registerTexture(v, 'diffuseMap', (t) => {
      this._diffuseMap = t;
      this.setState(true, 'diffuseMap');
      this.addMapParam("diffuseMap");
      this.update();
    })
    return this;
  }
  get diffuseMap () {
    return this._diffuseMap;
  }
  set specularMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'specularMap');
    this.registerTexture(v, 'specularMap', (t) => {
      this._specularMap = t;
      this.setState(true, 'specularMap');
      this.addMapParam("specularMap");
      this.update();
    })
    return this;
  }
  get specularMap () {
    return this._specularMap;
  }
  set normalMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'normalMap');
    this.registerTexture(v, 'normalMap', (t) => {
      this._normalMap = t;
      this.setState(true, 'normalMap');
      this.addMapParam("normalMap");
      this.update();
    })
    return this;
  }
  get normalMap () {
    return this._normalMap;
  }
  set displacementMap (v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, 'displacementMap');
    this.registerTexture(v, 'displacementMap', (t) => {
      this._displacementMap = t;
      this.setState(true, 'displacementMap');
      this.addMapParam("displacementMap");
      this.addParam("displacementScale");
      this.addParam("displacementBias")
      this.update();
    })
    return this;
  }
  get displacementMap () {
    return this._displacementMap;
  }
  set displacementScale (v) {
    this._displacementScale = v;
    this.update();
    return this;
  }
  get displacementScale () {
    return this._displacementScale;
  }
  set displacementBias (v) {
    this._displacementBias = v;
    this.update();
    return this;
  }
  get displacementBias () {
    return this._displacementBias;
  }
  set aoMapIntensity (v) {
    this._aoMapIntensity = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get aoMapIntensity () {
    return this._aoMapIntensity;
  }
  set color (v) {
    this._color = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get color () {
    return this._color;
  }
  set ambient (v) {
    this._ambient = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get ambient () {
    return this._ambient;
  }
  set diffuse (v) {
    this._diffuse = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get diffuse () {
    return this._diffuse;
  }
  set shininess (v) {
    this._shininess = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get shininess () {
    return this._shininess;
  }
  set specular (v) {
    this._specular = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get specular () {
    return this._specular;
  }
  set sdf (v) {
    this._sdf = v;
    this.addConfigParam("sdf", v);
    this.update();
    return this;
  }
  get sdf () {
    return this._sdf;
  }
  set cameraSide (v) {
    this._cameraSide = v;
    this.addConfigParam("cameraSide", v);
    this.update();
    return this;
  }
  get cameraSide () {
    return this._cameraSide;
  }
  set sdfFrom (v) {
    this._sdfFrom = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get sdfFrom () {
    return this._sdfFrom;
  }
  set sdfTo (v) {
    this._sdfTo = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get sdfTo () {
    return this._sdfTo;
  }
  updateState () {
    let state = (this.getState("map") || this.getState("map") == undefined)
      && (this.getState("alphaMap") || this.getState("alphaMap") == undefined)
      && (this.getState("aoMap") || this.getState("aoMap") == undefined)
      && (this.getState("emissiveMap") || this.getState("emissiveMap") == undefined)
      && (this.getState("ambientMap") || this.getState("ambientMap") == undefined)
      && (this.getState("diffuseMap") || this.getState("diffuseMap") == undefined)
      && (this.getState("specularMap") || this.getState("specularMap") == undefined)
      && (this.getState("normalMap") || this.getState("normalMap") == undefined);
    this.setState(state);
  }
  update () {
    if (!this.autoUpdate) {
      return;
    }
    if (!this.getState()) {
      this.updateState();
    }
    if (!this.getState()) {
      return;
    }
    let oldhash = this.hash;
    this.updateHash();
    let state=(oldhash != this.hash);
    let old=this.getState("layout");
    if(!old){
      this.setState(old?old:state, "layout");
    }
    this.notice();
  }
  toGPU () {
    super.toGPU()
    if (this.has('material')) {
      let matreialParameter = this.getParam('material');
      if (matreialParameter) {
        if (!matreialParameter.state) {
          let data = new Float32Array(24);
          data.set(color.format(this.color), 0);
          data.set(color.format(this.emissive, false), 4);
          data.set([this.opacity], 7);
          data.set(color.format(this.ambient, false), 8);
          data.set([this.aoMapIntensity], 11);
          data.set(color.format(this.diffuse, false), 12);
          data.set([this.alphaCutoff], 15);
          data.set(color.format(this.specular, false), 16);
          data.set([this.shininess], 19);
          data.set([this.defuseLight ? 1.0 : 0.0], 20);
          data.set([this.sdfFrom,this.sdfTo], 21);
          matreialParameter.buffer(data, 0);
        }
      } else {
        let data = new Float32Array(28);
        data.set(color.format(this.color), 0);
        data.set(color.format(this.emissive, false), 4);
        data.set([this.opacity], 7);
        data.set(color.format(this.ambient, false), 8);
        data.set([this.aoMapIntensity], 11);
        data.set(color.format(this.diffuse, false), 12);
        data.set([this.alphaCutoff], 15);
        data.set(color.format(this.specular, false), 16);
        data.set([this.shininess], 19);
        data.set([this.defuseLight ? 1.0 : 0.0], 20);
        data.set([this.sdfFrom,this.sdfTo], 21);
        matreialParameter = new Buffer({
          name: "material",
          usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
          visibility: GPUShaderStage.FRAGMENT,
          type: "uniform",
          size: 112,
          data
        })
        this.addParam("material", matreialParameter);
      }
      matreialParameter.state = true;
    }
    if (this.has("displacementBias")) {
      this.bufferToGPU(new Float32Array([this.displacementBias]), 'displacementBias', GPUShaderStage.VERTEX);
    }
    if (this.has("displacementScale")) {
      this.bufferToGPU(new Float32Array([this.displacementScale]), 'displacementScale', GPUShaderStage.VERTEX);
    }
    if (this.has("toneMappingExposure")) {
      this.bufferToGPU(new Float32Array([this.toneMappingExposure]), 'toneMappingExposure')
    }
    if (this.has("alphaMap")) {
      //const texture = textureLoader.textureMap.get(this.alphaMap);
      //const texture= await textureObject.toGPU();
      this.textureToGPU(this.alphaMap, "alphaMap");
    }
    if (this.has("aoMap")) {
      //const texture = textureLoader.textureMap.get(this.aoMap);
      //const texture= await textureObject.toGPU();
      this.textureToGPU(this.aoMap, "aoMap");
    }
    if (this.has("map")) {
      // const texture = textureLoader.textureMap.get(this.map);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.map, "map");
    }
    if (this.has("emissiveMap")) {
      //const texture = textureLoader.textureMap.get(this.emissiveMap);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.emissiveMap, "emissiveMap");
    }
    if (this.has("ambientMap")) {
      //const texture = textureLoader.textureMap.get(this.ambientMap);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.ambientMap, "ambientMap");
    }
    if (this.has("diffuseMap")) {
      //const texture = textureLoader.textureMap.get(this.diffuseMap);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.diffuseMap, "diffuseMap");
    }
    if (this.has("specularMap")) {
      // const texture = textureLoader.textureMap.get(this.specularMap);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.specularMap, "specularMap");
    }
    if (this.has("normalMap")) {
      // const texture = textureLoader.textureMap.get(this.normalMap);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.normalMap, "normalMap");
    }
    if (this.has("displacementMap")) {
      // const texture = textureLoader.textureMap.get(this.displacementMap);
      // const texture= await textureObject.toGPU();
      this.textureToGPU(this.displacementMap, "displacementMap",GPUShaderStage.VERTEX);
    }
    // 生成bindGroup和groupLayout 
    if (this.getState("layout")) {
      const params = this.getAllParams()
      let entriesGroup2 = [], entriesLayout2 = []
      let entriesGroup3 = [], entriesLayout3 = []
      this.defs={}
      this.config={}
      for (let index = 0; index < params.length; index++) {
        const param = params[index];
        this.defs[param.name]=param.value;
        if (param.group === 2) {
          entriesGroup2.push(param.gpu.entry)
          entriesLayout2.push(param.gpu.layout)
        }else if (param.group === 3) {
          entriesGroup3.push(param.gpu.entry)
          entriesLayout3.push(param.gpu.layout)
        }else{
          this.config[param.name]=param.value
        }
      }
      // 生成layout与bindgroup
      if (entriesGroup2.length > 0) {
        this.grouplayouts2 = Engine.instance.device.createBindGroupLayout({
          label: "layout" + 2,
          entries: entriesLayout2
        });
        this.bindGroup2 = Engine.instance.device.createBindGroup({
          label: "bindGroup" + 2,
          layout: this.grouplayouts2,
          entries: entriesGroup2
        });
      }
      if (entriesGroup3.length > 0) {
        this.grouplayouts3 = Engine.instance.device.createBindGroupLayout({
          label: "layout" + 3,
          entries: entriesLayout3
        });
        this.bindGroup3 = Engine.instance.device.createBindGroup({
          label: "bindGroup" + 3,
          layout: this.grouplayouts3,
          entries: entriesGroup3
        });
      }
      this.setState(false,"layout")
    }

  }
}
export { PointMaterial }